﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PaceR.Core.Interfaces.Validation;
using PaceR.Core.Interfaces;
using PaceR.Core.Interfaces.Conditionals;
using PaceR.Core.Logic;

namespace PaceR.Core.Validation
{
    public abstract class ValidatableField<T> : IValidatableField<T>
    {
        public virtual string FieldIdentifer { get; set; }
        public virtual T FieldValue { get; set; }
        public virtual bool IsRequired { get; set; }
        public virtual IValidationBounds<T> Bounds { get; set; }
        public virtual string ValidPattern { get; set; }
        public virtual string InvalidPattern { get; set; }

        public virtual IEnumerable<IConditionRule> BuildRules()
        {
            var rules = new List<IConditionRule>();
            if (IsRequired)
            {
                rules.Add(SimpleLogicRule.Define()
                    .If(SimpleLogicTest<T>.TestValue(FieldValue).IsDefault().IsTrue(),
                    //then
                    new SimpleValidationMessageOperation()
                    {
                        Function = v => BuildMessage(v),
                        FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.Required)
                    }));
            }
            //bounds
            if (Bounds != null)
            {
                //value bounds
                if (!EqualityComparer<T>.Default.Equals(Bounds.LowerBound, default(T))
                    && !EqualityComparer<T>.Default.Equals(Bounds.UpperBound, default(T)))
                {
                    //test within bounds
                    rules.Add(SimpleLogicRule.Define()
                        .If(SimpleLogicTest<T>.TestValue(FieldValue).IsOutsideRange(Bounds.LowerBound, Bounds.UpperBound).IsTrue()
                        //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.WithinRange)
                        }));
                }
                else if (!EqualityComparer<T>.Default.Equals(Bounds.LowerBound, default(T)))
                {
                    //test lower bound
                    rules.Add(SimpleLogicRule.Define()
                        .If(SimpleLogicTest<T>.TestValue(FieldValue).IsLessThan(Bounds.LowerBound).IsTrue()
                        //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.GreaterThan)
                        }));
                }
                else if (!EqualityComparer<T>.Default.Equals(Bounds.UpperBound, default(T)))
                {
                    //test upper bound
                    rules.Add(SimpleLogicRule.Define()
                        .If(SimpleLogicTest<T>.TestValue(FieldValue).IsGreaterThan(Bounds.UpperBound).IsTrue()
                        //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.LessThan)
                        }));
                }
                //string length bounds
                int length = (FieldValue != null) ? FieldValue.ToString().Length : 0;
                if (Bounds.StringMaxLength > 0)
                {
                    rules.Add(SimpleLogicRule.Define()
                        .If(SimpleLogicTest<int>.TestValue(length).IsGreaterThan(Bounds.StringMaxLength)
                            .Or().Test(length).Equals(Bounds.StringMaxLength).IsTrue()
                        //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.LessThan)
                        }));
                }
                if (Bounds.StringMinLength > 0)
                {
                    rules.Add(SimpleLogicRule.Define()
                        .If(SimpleLogicTest<int>.TestValue(length).IsLessThan(Bounds.StringMinLength)
                            .Or().Test(length).Equals(Bounds.StringMinLength).IsTrue()
                        //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.GreaterThan)
                        }));
                }
            }
            string fieldValueText = FieldValue != null ? FieldValue.ToString() : string.Empty;
            //Pattern Matching
            if (!string.IsNullOrEmpty(ValidPattern))
            {
                rules.Add(SimpleLogicRule.Define()
                    .If(SimpleLogicTest<T>.TestValue(FieldValue).IsNotDefault().IsTrue(),
                    //Then
                        SimpleLogicRule.Define()
                        .If(SimpleLogicTest<string>.TestValue(fieldValueText).DoesNotMatch(ValidPattern).IsTrue()
                    //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.Matches)
                        })));
            }
            //Invalid Pattern
            if (!string.IsNullOrEmpty(InvalidPattern))
            {
                rules.Add(SimpleLogicRule.Define()
                    .If(SimpleLogicTest<T>.TestValue(FieldValue).IsNotDefault().IsTrue(),
                    //Then
                        SimpleLogicRule.Define()
                        .If(SimpleLogicTest<string>.TestValue(fieldValueText).Matches(InvalidPattern).IsTrue()
                    //then
                        , new SimpleValidationMessageOperation()
                        {
                            Function = v => BuildMessage(v),
                            FunctionArgument = new Tuple<string, ConstraintOperators>(FieldIdentifer, ConstraintOperators.Matches)
                        })));
            }
            //return
            return rules;
        }

        public virtual IValidatableField<T> SetRequired()
        {
            this.IsRequired = true;
            return this;
        }

        public virtual IValidatableField<T> SetBounds(IValidationBounds<T> bounds)
        {
            this.Bounds = bounds;
            return this;
        }

        public virtual IValidatableField<T> SetValidPattern(string pattern)
        {
            this.ValidPattern = pattern;
            return this;
        }

        public virtual IValidatableField<T> SetInvalidPattern(string pattern)
        {
            this.InvalidPattern = pattern;
            return this;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ruleData">first value is Field Identifier and the second is the Constraint that failed</param>
        /// <returns></returns>
        protected abstract IResultMessages BuildMessage(Tuple<string, ConstraintOperators> ruleData);
    }
}
